package simplega;
// Genetic Algorithm Java classes
//
// Copyright 1996-2008, Mark Watson.  All rights reserved.

import java.util.*;

abstract public class SimpleGeneticAlgorithm {

	protected int numGeneOfChrom;
	protected int numChromosome;
	
	protected List<Chromosome> chromosomes;
	private float probCrossover;
	private float probMutation;
	private RoulleteWheel roullete;
	
	public SimpleGeneticAlgorithm(int numofgene, int numofchromo,
			float crossover_fraction, float mutation_fraction) {
		numGeneOfChrom = numofgene;
		numChromosome = numofchromo;
		probCrossover = crossover_fraction;
		probMutation = mutation_fraction;
		chromosomes = new ArrayList<Chromosome>(numofchromo);		
		
		//generate Population
		initData(numofchromo, numofgene);
		
		//sort for the best fit individual
		sort();
		roullete = new RoulleteWheel(numofchromo, numofgene);
	}
	
	public void initData(int numofchromo, int numofgene){
		for (int i = 0; i < numofchromo; i++) {
			this.chromosomes.add(new Chromosome(numofgene));
			for (int j = 0; j < numofgene; j++) {
				this.chromosomes.get(i).setBit(j, Math.random() < 0.5);
			}
		}
	}
	
	public void sort() {
		Collections.sort(chromosomes, new ChromosomeComparator());
	}

	public boolean getGene(int chromosome, int gene) {
		return chromosomes.get(chromosome).getBit(gene);
	}

	public void setGene(int chromosome, int gene, int value) {
		chromosomes.get(chromosome).setBit(gene, value != 0);
	}

	public void setGene(int chromosome, int gene, boolean value) {
		chromosomes.get(chromosome).setBit(gene, value);
	}

	public void evolve() {
		//calcFitness();
		//sort();
		crossover();
		mutate();
		removeDuplicates();
		calcFitness();
		sort();
	}

	public void crossover() {
		int num = (int) (numChromosome * probCrossover);
		for (int i = num - 1; i >= 0; i--) {
			
			int c1 = 1 + (int) ((roullete.getRoulleteWheelSize() - 1) * Math.random() * 0.9999f);
			int c2 = 1 + (int) ((roullete.getRoulleteWheelSize() - 1) * Math.random() * 0.9999f);
			c1 = roullete.getRoulleteWheelSlot(c1);
			c2 = roullete.getRoulleteWheelSlot(c2);
			if (c1 != c2) {
				int locus = 1 + (int) ((numGeneOfChrom - 2) * Math
						.random());
				for (int g = 0; g < numGeneOfChrom; g++) {
					if (g < locus) {
						setGene(i, g, getGene(c1, g));
					} else {
						setGene(i, g, getGene(c2, g));
					}
				}
			}
		}
	}

	public void mutate() {
		int num = (int) (numChromosome* probMutation);
		for (int i = 0; i < num; i++) {
			
			int c = 1 + (int) ((numChromosome - 1) * Math.random() * 0.99);
			int g = (int) (numGeneOfChrom * Math.random() * 0.99);
			setGene(c, g, !getGene(c, g));
		}
	}

	public void removeDuplicates() {
		for (int i = numChromosome - 1; i > 1; i--) {
			for (int j = 0; j < i; j++) {
				if (chromosomes.get(i).equals(chromosomes.get(j))) {
					int g = (int) (numGeneOfChrom * Math.random() * 0.99);
					setGene(i, g, !getGene(i, g));
					break;
				}
			}
		}
	}

	// Override the following function in sub-classes:
	abstract public void calcFitness();	
	
	abstract public void calcStatistic();
	
}


class ChromosomeComparator implements Comparator<Chromosome> {
	public int compare(Chromosome c1, Chromosome c2) {
		return  ((c2.getFitness() - c1.getFitness()));
	}
}